/*********************************************************************************
 *      Copyright:  (C) 2025 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  main.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(2025年04月29日)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "2025年04月29日 11时20分36秒"
 *                 
 ********************************************************************************/

//#include <jansson.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <ctype.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/resource.h>
#include <sys/epoll.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <signal.h>
#include <getopt.h>
#include <libgen.h>

#include "logger.h"
#include "sqlite3_handler.h"

#define TLV_HEADER 		0xFEED
#define TAG_TEMPERATURE 1
#define TLV_MINSIZE 	20 // 估计一个最小长度
#define DB_FILE 		"server_data.db"
#define MAX_EVENTS 		512
#define ARRAY_SIZE(x) 	(sizeof(x)/sizeof(x[0]))
#define CRC16_ITU_T_POLY 0X1021

static inline void print_usage(char *progname);
int socket_server_init(char *listen_ip, int listen_port);
void set_socket_rilimit(void);
void handle_client_data(int fd);//处理数据


//CRC校验
uint16_t crc_itu_t(const uint8_t *data, size_t length)
{
    uint16_t crc = 0xFFFF;
    for (size_t i = 0; i < length; i++)
    {
        crc ^= ((uint16_t)data[i] << 8);
        for (int j = 0; j < 8; j++)
        {
            if (crc & 0x8000)
                crc = (crc << 1) ^ CRC16_ITU_T_POLY;
            else
                crc <<= 1;
        }
    }
    return crc;
}

int main(int argc,char **argv)
{
	int 				opt;
	int 				listenfd, connfd;
	char 				*progname = NULL;
	int 				serv_port = 0;
	int 				daemon_run = 0;//默认非后台运行
	struct epoll_event	event;
	struct epoll_event  event_array[MAX_EVENTS];
	int					rv;
	int 				i,j;
	int 				found;
	int 				epollfd;
	int 				events;
	char 				buf[1024];
	struct option		long_options[]=
	{
		{"daemon",no_argument,NULL,'b'},
		{"port",required_argument,NULL,'p'},
		{"help",no_argument,NULL,'h'},
		{NULL,0,NULL,0}
	};

	log_open("console", LOG_LEVEL_DEBUG, 0, 1);//日志

	progname = basename(argv[0]);

	while((opt=getopt_long(argc, argv, "bp:h", long_options, NULL)) != -1)
	{
		switch(opt)
		{
			case 'b':
				daemon_run = 1;
				break;
			case 'p':
				serv_port=atoi(optarg);
				break;
			case 'h':
				print_usage(progname);
				return EXIT_SUCCESS;
			default:
				break;
		}
	}

	if(db_init(DB_FILE) != 0)//初始化数据库
	{
		log_error("Database init failed!\n");
		return -1;
	}

	if(!serv_port)
	{
		print_usage(progname);
		return -1;
	}

	set_socket_rilimit();//设置最大socketfd
	
	if((listenfd = socket_server_init(NULL, serv_port)) < 0)
	{
		log_error("ERROR:%s server listen on port %d failure\n", argv[0], serv_port);
		return -2;
	}
	log_info("%s servr start to listen on port %d\n", argv[0], serv_port);
	
	//后台运行
	if(daemon_run)
	{
		daemon(0,0);
	}

	if((epollfd = epoll_create(MAX_EVENTS)) < 0)
	{
		log_error("epoll_create() failure:%s\n", strerror(errno));
		return -3;
	}

	event.events = EPOLLIN;//监听可读事件
	event.data.fd = listenfd;//将监听socket的文件描述符存入~.fd,当epoll_wait返回时。可以通过events[i].data.fd获取是哪个fd触发了事件

	//将listenfd注册到epoll实例中，告诉epoll需要监听这个套接字上的可读事件
	if(epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &event) < 0)
	{
		log_error("epoll add listen socket failure:%s\n", strerror(errno));
		return -4;
	}

	for( ; ; )
	{
		//在此阻塞直到rpoll实例中的文件描述符有事件发生
		events = epoll_wait(epollfd, event_array, MAX_EVENTS, -1);
		if(events < 0)//failure
		{
			log_error("epoll failure:%s\n", strerror(errno));
			break;
		}
		else if(events == 0)//timeout
		{
			log_debug("epoll get timeout\n");
			continue;//retry			
		}

		for(int i=0; i<events; i++)
		{
			if((event_array[i].events & EPOLLERR) || (event_array[i].events & EPOLLHUP))
			{
				log_error("epoll_wait get error on fd:%d :%s\n", event_array[i].data.fd, strerror(errno));
				epoll_ctl(epollfd, EPOLL_CTL_DEL, event_array[i].data.fd, NULL);
				close(event_array[i].data.fd);
			}

			if(event_array[i].data.fd == listenfd)//new client
			{
				if((connfd = accept(listenfd, (struct sockaddr *)NULL, NULL)) < 0)
				{
					log_warn("accept new client failure:%s\n", strerror(errno));
					continue;
				}
				event.data.fd = connfd;
				event.events = EPOLLIN;

				//将新的客户端添加到epoll实例中，以便监控该客户端的事件
				if(epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &event) < 0)
				{
					log_warn("epoll add client socket failure:%s\n", strerror(errno));
					close(event_array[i].data.fd);
					continue;
				}
				log_info("epoll add new client socket[%d] ok\n", connfd);
			}
			else//已加入的客户端发生事件
			{
				handle_client_data(event_array[i].data.fd);//打印并加入数据库（有格式要求）
			}
		}
	

	}

	close(listenfd);
	db_close();
	log_close();
	return 0;
}
static inline void print_usage(char *progname)
{
	log_info("usage:%s [option]...\n",progname);

	log_info("-d [daemon] set program running on background\n");
	log_info("-p [port] socket server port address\n");
	log_info("-h [-help] display this help information\n");

	log_info("\nexample:%s -d -p 8880\n",progname);

	return ;
}


void set_socket_rilimit(void)
{
	struct rlimit limit = {0};

	getrlimit(RLIMIT_NOFILE, &limit);
	limit.rlim_cur = limit.rlim_max;
	setrlimit(RLIMIT_NOFILE, &limit);

	log_info("set socket open fd max count to %ld\n", limit.rlim_max);
}

int socket_server_init(char *listen_ip, int listen_port)
{
	struct sockaddr_in	servaddr;
	int					rv = 0;
	int					on = 1;
	int					listenfd;

	if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
	{
		log_error("use socket() to create a TCP socket failure:%s\n", strerror(errno));
		return -1;
	}

	//允许立即重用端口
	setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

	memset(&servaddr, 0, sizeof(servaddr));
	servaddr.sin_family = AF_INET;//ipv4
	servaddr.sin_port = htons(listen_port);

	if(!listen_ip)//监听所有ip
	{
		servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	}
	else//监听特定ip
	{
		if(inet_pton(AF_INET, listen_ip, &servaddr.sin_addr) <= 0)
		{
			log_error("ient_pton() set listen ip address failure.\n");
			rv = -2;
			goto cleanup;
		}
	}

	if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
	{
		log_error("use bind() to bind the TCP socket failure:%s\n", strerror(errno));
		rv = -3;
		goto cleanup;
	}

	if(listen(listenfd, 64) < 0)
	{
		log_error("use listen() to listen TCP socket failure:%s", strerror(errno));
		rv = -4;
		goto cleanup;
	}

cleanup:
	if(rv < 0)
		close(listenfd);
	else
		rv = listenfd;


	return rv;

}



void handle_client_data(int fd)
{
    char 	buf[1024] = {0};
    int 	rv = read(fd, buf, sizeof(buf) - 1);
    if (rv <= 0)
    {
        if (rv < 0)
            log_error("read error\n");
        else
            log_info("client disconnected: fd=%d\n", fd);
        close(fd);
        return;
    }

    buf[rv] = '\0';
    log_debug("Received [%s] from fd:[%d]\n", buf, fd);

    // 判断格式
    if (strchr(buf, '+'))
    {
        // 老格式：time+id+temp
        char *time = strtok(buf, "+");
        char *device_id = strtok(NULL, "+");
        char *temp = strtok(NULL, "+");

        if (time && device_id && temp)
        {
            double temperature = atof(temp);
            if (db_insert_record(time, device_id, temperature) == 0)
                log_info("Saved to database: time=%s ,id=%s, temp=%.5f\n", time, device_id, temperature);
            else
                log_error("Failed to save to database\n");
        }
        else
            log_warn("Invalid data format, not saved\n");
    }
    
	else if (buf[0] == '{')//JSON format
	{
		// 手动解析JSON
		char *p_time = strstr(buf, "\"time\":\"");
		char *p_id   = strstr(buf, "\"device_id\":\"");
		char *p_temp = strstr(buf, "\"temperature\":\"");

		char time[32] = {0};
		char device_id[20] = {0};
		char temperature[16] = {0};

		if (p_time && p_id && p_temp)
		{
			sscanf(p_time, "\"time\":\"%31[^\"]\"", time);
			sscanf(p_id, "\"device_id\":\"%19[^\"]\"", device_id);
			sscanf(p_temp, "\"temperature\":\"%15[^\"]\"", temperature);

			double temp_val = atof(temperature);
			if (db_insert_record(time, device_id, temp_val) == 0)
				log_info("Saved to database (JSON): time=%s ,id=%s, temp=%.5f\n", time, device_id, temp_val);
			else
				log_error("Failed to save JSON to database\n");
		}
		else
		{
			log_warn("JSON format error, missing field\n");
		}
	}	

	else//TLV format
	{
		uint8_t *data = (uint8_t *)buf;

		if (rv < TLV_MINSIZE)
		{
			log_warn("TLV data too short\n");
			return;
		}

		uint16_t header = (data[0] << 8) | data[1];
		uint8_t tag = data[2];
		uint16_t length = (data[3] << 8) | data[4];
		
		if (header != TLV_HEADER)
		{
			log_warn("Invalid TLV header\n");
			return;
		}

		if (tag != TAG_TEMPERATURE || length + 5 + 2 > rv)
		{
			log_warn("Invalid TLV tag or length\n");
			return;
		}

		// 校验 CRC（计算前 length+5 个字节，不含 CRC）
		uint16_t received_crc = (data[rv - 2] << 8) | data[rv - 1];
		uint16_t calc_crc = crc_itu_t(data, rv - 2);

		if (received_crc != calc_crc)
		{
			log_warn("CRC check failed! received=0x%04X, calculated=0x%04X\n",
					received_crc, calc_crc);
			return;
		}
		log_info("CRC check passed.\n");

		// 提取时间
		char time_buf[20];
		snprintf(time_buf, sizeof(time_buf), "20%02d-%02d-%02d %02d:%02d:%02d",
				data[5], data[6], data[7], data[8], data[9], data[10]);

		// 提取 device_id
		char device_id[20];
		strncpy(device_id, (char *)&data[11], 8);
		device_id[8] = '\0';

		// 提取温度
		float temperature = data[19] + (data[20] / 100.0);


		if (db_insert_record(time_buf, device_id, temperature) == 0)
			log_info("Saved to database (TLV): time=%s ,id=%s, temp=%.2f\n",
					time_buf, device_id, temperature);
		else
			log_error("Failed to save TLV to database\n");
	}
}
